package com.ssy.jt808.gateway.application.service;

import com.ssy.jt808.gateway.domain.converter.LocationDataConverter;
import com.ssy.jt808.gateway.domain.converter.T0200MessageConverter;
import com.ssy.jt808.gateway.domain.pojo.TerminalInfo;
import com.ssy.jt808.gateway.domain.service.TerminalService;
import com.ssy.jt808.gateway.infrastructure.gateway.TerminalMessagePublisher;
import com.ssy.jt808.gateway.sdk.terminal.message.TerminalMessageBase;
import com.ssy.jt808.gateway.sdk.terminal.message.alarm.TerminalAlarmBackoffMessage;
import com.ssy.jt808.gateway.sdk.terminal.message.alarm.TerminalAlarmMessage;
import com.ssy.jt808.gateway.sdk.terminal.message.connection.TerminalOfflineMessage;
import com.ssy.jt808.gateway.sdk.terminal.message.connection.TerminalOnlineMessage;
import com.ssy.jt808.gateway.sdk.terminal.message.location.LocationBackoffMessage;
import com.ssy.jt808.gateway.sdk.terminal.message.location.LocationMessage;
import com.ssy.jt808.gateway.sdk.terminal.message.location.T0200DataMessage;
import com.ssy.jt808.gateway.sdk.terminal.pojo.info.LocationInfo;
import com.ssy.jt808.gateway.sdk.terminal.pojo.info.alarm.TerminalAlarmInfo;
import com.ssy.jt808.gateway.util.LocalIpUtils;
import com.ssy.jt808.protocol.t808.T0200;
import com.ssy.jt808.protocol.t808.T0704;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author wangzezhou
 */
@Slf4j
@Service
public class TerminalAppService{

  private String localHost;

  private TerminalMessagePublisher terminalMessagePublisher;

  private TerminalService terminalService;

  /**
   * 上线处理
   *
   * @return 通讯 token
   */
  public String onOnline( TerminalInfo terminalInfo ){

    String token = terminalService.onOnline(terminalInfo);

    TerminalOnlineMessage onlineMessage = new TerminalOnlineMessage(localHost);
    updateTerminalInfo(onlineMessage, terminalInfo);
    terminalMessagePublisher.sendConnectionStatus(onlineMessage, onlineMessage.getTerminalId());
    return token;
  }

  public TerminalInfo onVerifyToken( String terminalPhoneNo, String verifyToken ){

    TerminalInfo terminalInfo = terminalService.doVerifyToken(terminalPhoneNo, verifyToken);

    TerminalOnlineMessage onlineMessage = new TerminalOnlineMessage(localHost);
    updateTerminalInfo(onlineMessage, terminalInfo);
    terminalMessagePublisher.sendConnectionStatus(onlineMessage, onlineMessage.getTerminalId());
    return terminalInfo;
  }

  public void onOffline( String terminalPhoneNo ){
    TerminalInfo terminalInfo = terminalService.getTerminalInfo(terminalPhoneNo);
    TerminalOfflineMessage offlineMessage = new TerminalOfflineMessage(localHost);
    updateTerminalInfo(offlineMessage, terminalInfo);
    terminalMessagePublisher.sendConnectionStatus(offlineMessage, terminalInfo.getTerminalId());
  }

  /**
   * 定位上报
   *
   * @param t0200s
   */
  public void onLocation( List<T0200> t0200s ){

    Map<Long,LocationMessage> locationsMsgMap = new TreeMap<>();
    Map<Long,TerminalAlarmMessage> alarmMessageMap = new TreeMap<>();

    T0200DataMessage t0200DataMessage = new T0200DataMessage();

    for(T0200 t0200 : t0200s){

      TerminalInfo terminalInfo = terminalService.getTerminalInfo(t0200.getClientId());
      LocationInfo locationInfo = new LocationInfo();
      TerminalAlarmInfo alarmInfo = new TerminalAlarmInfo();

      try{
        LocationDataConverter.extractLocationInfo(t0200, locationInfo, alarmInfo);
      } catch(Exception e){
        log.error("定位数据信息提取异常：" + ByteBufUtil.hexDump(t0200.getPayload()), e);
      }

      LocationMessage locationMessage = doGetLocationMessage(locationsMsgMap, terminalInfo);
      locationMessage.addLocation(locationInfo);

      TerminalAlarmMessage alarmMessage = doGetAlarmMessage(alarmMessageMap, terminalInfo);
      alarmMessage.addAlarmInfo(alarmInfo);

      ByteBuf payload = t0200.getPayload();
      try{
        T0200MessageConverter.updateT0200DataMessage(t0200DataMessage, payload, terminalInfo);
      } catch(Exception e){
        log.error("T0200 定位数据转换异常：" + ByteBufUtil.hexDump(payload), e);
        payload.release();
        continue;
      }

      payload.release();
      terminalMessagePublisher.sendT0200Messsage(t0200DataMessage, terminalInfo.getTerminalId());
    }

    locationsMsgMap.forEach(( k, v )->terminalMessagePublisher.sendLocations(v, v.getTerminalId()));

    alarmMessageMap.forEach(( k, v )->{
      if( CollectionUtils.isEmpty(v.getAlarmInfos()) ){
        return;
      }
      terminalMessagePublisher.sendAlarmInfo(v, v.getTerminalId());
    });
  }

  private LocationMessage doGetLocationMessage( Map<Long,LocationMessage> locationMessageMap,
                                                TerminalInfo terminalInfo ){

    Long terminalId = terminalInfo.getTerminalId();
    LocationMessage locationMessage = locationMessageMap.get(terminalId);
    if( locationMessage == null ){
      locationMessage = new LocationMessage();
      updateTerminalInfo(locationMessage, terminalInfo);
      locationMessageMap.put(terminalId, locationMessage);
    }

    return locationMessage;
  }

  private TerminalAlarmMessage doGetAlarmMessage( Map<Long,TerminalAlarmMessage> alarmMessageMap,
                                                  TerminalInfo terminalInfo ){

    Long terminalId = terminalInfo.getTerminalId();
    TerminalAlarmMessage alarmMessage = alarmMessageMap.get(terminalId);
    if( alarmMessage == null ){
      alarmMessage = new TerminalAlarmMessage();
      updateTerminalInfo(alarmMessage, terminalInfo);
      alarmMessageMap.put(terminalId, alarmMessage);
    }

    return alarmMessage;
  }

  /**
   * 盲区补偿
   *
   * @param t0704
   */
  public void onLocationBackoff( T0704 t0704 ){

    List<T0200> t0200s = t0704.getItems();
    if( CollectionUtils.isEmpty(t0200s) ){
      return;
    }

    TerminalInfo terminalInfo = terminalService.getTerminalInfo(t0704.getClientId());

    LocationMessage locationMessage;
    TerminalAlarmMessage alarmMessage;
    if( t0704.getType() == 0 ){
      locationMessage = new LocationMessage(t0200s.size());
      updateTerminalInfo(locationMessage, terminalInfo);
      alarmMessage = new TerminalAlarmMessage();
      updateTerminalInfo(alarmMessage, terminalInfo);
    }
    else{
      locationMessage = new LocationBackoffMessage(t0200s.size());
      updateTerminalInfo(locationMessage, terminalInfo);
      alarmMessage = new TerminalAlarmBackoffMessage();
      updateTerminalInfo(alarmMessage, terminalInfo);
    }

    LocationInfo locationInfo;
    TerminalAlarmInfo alarmInfo;
    for(T0200 t0200 : t0200s){
      locationInfo = new LocationInfo();
      alarmInfo = new TerminalAlarmInfo();

      LocationDataConverter.extractLocationInfo(t0200, locationInfo, alarmInfo);
      locationMessage.addLocation(locationInfo);
      alarmMessage.addAlarmInfo(alarmInfo);
    }

    terminalMessagePublisher.sendLocations(locationMessage, locationMessage.getTerminalId());
    if( !CollectionUtils.isEmpty(alarmMessage.getAlarmInfos()) ){
      terminalMessagePublisher.sendAlarmInfo(alarmMessage, alarmMessage.getTerminalId());
    }
  }

  private void updateTerminalInfo( TerminalMessageBase message, TerminalInfo terminalInfo ){

    message.setDriverId(message.getDriverId());
    message.setPhoneNo(terminalInfo.getPhoneNo());
    message.setTerminalId(terminalInfo.getTerminalId());
    message.setVehicleId(terminalInfo.getVehicleId());
    message.setPhoneNo(terminalInfo.getPhoneNo());
    message.setPlateNo(terminalInfo.getPlateNo());
    message.setVehicleOrgId(terminalInfo.getVehicleOrgId());
    message.setVehicleOrgAncestors(terminalInfo.getOrgAncestors());

  }


  @Autowired
  public void setTerminalMessagePublisher( TerminalMessagePublisher terminalMessagePublisher ){
    this.terminalMessagePublisher = terminalMessagePublisher;
  }

  @Autowired
  public void setLocalHost( @Value("${jt808-gateway.vehicle-management.host}") String host,
                            @Value("${jt808-gateway.vehicle-management.port}") int port,
                            @Value("${server.port}") int httpServicePort ){
    this.localHost = LocalIpUtils.getLocalIp4String(host, port) + ":" + httpServicePort;
  }

  @Autowired
  public void setTerminalService( TerminalService terminalService ){
    this.terminalService = terminalService;
  }
}
